/*
 ABOUT THE FIRMWARE
 rainbowduino_firmware_rigel - alternative firmware for Rainbowduino boards produced by Seeed Studio
 Copyright (c) 2011 Vichaya Sidhipong, bsidhipong@gmail.com

 This file is part of rainbowduino_firmware_rigel.

 rainbowduino_firmware_rigel is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 rainbowduino_firmware_rigel is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with rainbowduino_firmware_rigel.  If not, see <http://www.gnu.org/licenses/

 VERSION HISTORY
 0.1     5/24/2011 initial release under GPLv3

*/

#include <Wire.h>
#include <avr/pgmspace.h>
#include "i2c_commands.h"
#include "rainbowduino.h"

typedef uint8_t gamma_set_t[32]; /* gamma-correction table for 32 brightness levels */

/* gamma-correction tables */
gamma_set_t gamma_sets[]= { /* define the Gamma value for correct the different LED matrix */
    { 0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7 }, /* default (linear) */
    { 0xFF,0xFF,0xFE,0xFE,0xFD,0xFD,0xFC,0xFC,0xFB,0xFB,0xF9,0xF9,0xF7,0xF7,0xF5,0xF5,0xF3,0xF3,0xF0,0xF0,0xED,0xED,0xEA,0xEA,0xE7,0xE7,0xE4,0xE4,0xE1,0xE1,0xDD,0xDD }, /* preferred (progressive) */
};
uint8_t chosen_gamma = 1; /* current gamma-ramp in use (index of gamma_sets array) */
#define COUNT_OF_GAMMA_SETS (sizeof(gamma_sets)/sizeof(gamma_set_t))

#define elementsof(name,type) (sizeof(name)/sizeof(type))
typedef rgb_triplet_t rgbframe_t[64];
rgbframe_t frame_buffers[] = {
	/* primary buffer */
	{
		{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
		{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
		{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
		{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
		{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
		{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
		{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},
		{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00},{0x00,0x00,0x00}
	},
	/* secondary buffer (rgb gradation) */
	{
		{0x00,0x00,0x0F},{0x06,0x00,0x0F},{0x0E,0x00,0x0F},{0x0F,0x00,0x0D},{0x0F,0x00,0x07},{0x0F,0x00,0x01},{0x0F,0x04,0x00},{0x0F,0x0B,0x00},
		{0x06,0x00,0x0F},{0x0E,0x00,0x0F},{0x0F,0x00,0x0D},{0x0F,0x00,0x07},{0x0F,0x00,0x01},{0x0F,0x04,0x00},{0x0F,0x0B,0x00},{0x0F,0x0F,0x00},
		{0x0E,0x00,0x0F},{0x0F,0x00,0x0D},{0x0F,0x00,0x0F},{0x0F,0x00,0x01},{0x0F,0x04,0x00},{0x0F,0x0B,0x00},{0x0F,0x0F,0x00},{0x0A,0x0F,0x00},
		{0x0F,0x00,0x0D},{0x0F,0x00,0x0A},{0x0F,0x00,0x01},{0x0F,0x04,0x00},{0x0F,0x0B,0x00},{0x0F,0x0F,0x00},{0x0A,0x0F,0x00},{0x03,0x0F,0x00},
		{0x0F,0x00,0x07},{0x0F,0x00,0x01},{0x0F,0x04,0x00},{0x0F,0x0B,0x00},{0x0F,0x0F,0x00},{0x0A,0x0F,0x00},{0x03,0x0F,0x00},{0x00,0x0F,0x01},
		{0x0F,0x00,0x01},{0x0F,0x04,0x00},{0x0F,0x0B,0x00},{0x0A,0x0F,0x00},{0x0A,0x0F,0x00},{0x03,0x0F,0x00},{0x00,0x0F,0x01},{0x00,0x0F,0x07},
		{0x0F,0x04,0x00},{0x0F,0x0B,0x00},{0x0F,0x0F,0x00},{0x0A,0x0F,0x00},{0x03,0x0F,0x00},{0x00,0x0F,0x01},{0x00,0x0F,0x07},{0x00,0x0F,0x0E},
		{0x0F,0x0B,0x00},{0x0F,0x0F,0x00},{0x0A,0x0F,0x00},{0x03,0x0F,0x00},{0x00,0x0F,0x01},{0x00,0x0F,0x07},{0x00,0x0F,0x0E},{0x00,0x0D,0x0F}
	},
	/* calibration */
	{
		{0x00,0x00,0x00},{0x01,0x00,0x00},{0x02,0x00,0x00},{0x03,0x00,0x00},{0x04,0x00,0x00},{0x05,0x00,0x00},{0x06,0x00,0x00},{0x07,0x00,0x00},
		{0x08,0x00,0x00},{0x09,0x00,0x00},{0x0a,0x00,0x00},{0x0b,0x00,0x00},{0x0c,0x00,0x00},{0x0d,0x00,0x00},{0x0e,0x00,0x00},{0x0f,0x00,0x00},
		{0x10,0x00,0x00},{0x11,0x00,0x00},{0x12,0x00,0x00},{0x13,0x00,0x00},{0x14,0x00,0x00},{0x15,0x00,0x00},{0x16,0x00,0x00},{0x17,0x00,0x00},
		{0x18,0x00,0x00},{0x19,0x00,0x00},{0x1a,0x00,0x00},{0x1b,0x00,0x00},{0x1c,0x00,0x00},{0x1d,0x00,0x00},{0x1e,0x00,0x00},{0x1f,0x00,0x00},
		{0x00,0x00,0x00},{0x00,0x00,0x01},{0x00,0x00,0x02},{0x00,0x00,0x03},{0x00,0x00,0x04},{0x00,0x00,0x05},{0x00,0x00,0x06},{0x00,0x00,0x07},
		{0x00,0x00,0x08},{0x00,0x00,0x09},{0x00,0x00,0x0a},{0x00,0x00,0x0b},{0x00,0x00,0x0c},{0x00,0x00,0x0d},{0x00,0x00,0x0e},{0x00,0x00,0x0f},
		{0x00,0x00,0x10},{0x00,0x00,0x11},{0x00,0x00,0x12},{0x00,0x00,0x13},{0x00,0x00,0x14},{0x00,0x00,0x15},{0x00,0x00,0x16},{0x00,0x00,0x17},
		{0x00,0x00,0x18},{0x00,0x00,0x19},{0x00,0x00,0x1a},{0x00,0x00,0x1b},{0x00,0x00,0x1c},{0x00,0x00,0x1d},{0x00,0x00,0x1e},{0x00,0x00,0x1f}
	},
	{
		{0x00,0x00,0x00},{0x01,0x00,0x00},{0x02,0x00,0x00},{0x03,0x00,0x00},{0x04,0x00,0x00},{0x05,0x00,0x00},{0x06,0x00,0x00},{0x07,0x00,0x00},
		{0x08,0x00,0x00},{0x09,0x00,0x00},{0x0a,0x00,0x00},{0x0b,0x00,0x00},{0x0c,0x00,0x00},{0x0d,0x00,0x00},{0x0e,0x00,0x00},{0x0f,0x00,0x00},
		{0x10,0x00,0x00},{0x11,0x00,0x00},{0x12,0x00,0x00},{0x13,0x00,0x00},{0x14,0x00,0x00},{0x15,0x00,0x00},{0x16,0x00,0x00},{0x17,0x00,0x00},
		{0x18,0x00,0x00},{0x19,0x00,0x00},{0x1a,0x00,0x00},{0x1b,0x00,0x00},{0x1c,0x00,0x00},{0x1d,0x00,0x00},{0x1e,0x00,0x00},{0x1f,0x00,0x00},
		{0x00,0x00,0x00},{0x00,0x01,0x00},{0x00,0x02,0x00},{0x00,0x03,0x00},{0x00,0x04,0x00},{0x00,0x05,0x00},{0x00,0x06,0x00},{0x00,0x07,0x00},
		{0x00,0x08,0x00},{0x00,0x09,0x00},{0x00,0x0a,0x00},{0x00,0x0b,0x00},{0x00,0x0c,0x00},{0x00,0x0d,0x00},{0x00,0x0e,0x00},{0x00,0x0f,0x00},
		{0x00,0x10,0x00},{0x00,0x11,0x00},{0x00,0x12,0x00},{0x00,0x13,0x00},{0x00,0x14,0x00},{0x00,0x15,0x00},{0x00,0x16,0x00},{0x00,0x17,0x00},
		{0x00,0x18,0x00},{0x00,0x19,0x00},{0x00,0x1a,0x00},{0x00,0x1b,0x00},{0x00,0x1c,0x00},{0x00,0x1d,0x00},{0x00,0x1e,0x00},{0x00,0x1f,0x00}
	},
};
#define COUNT_OF_FRAMEBUFFERS elementsof(frame_buffers,rgbframe_t)

uint8_t view = 0; /* buffer to hold currently-visible frame */
uint8_t draw = 1; /* drawing occurs in back buffer */
uint8_t refresh = 0; /* interrupt-service routine uses this index to avoid flickering if buffer-swapping occurs during matrix refresh */

/* interrupt-service routine uses these variables to refresh matrix */
volatile uint8_t scan_line = 0, brightness_level = 0;

/* current paper (background) color, defaults to black */
byte paper_red = 0;
byte paper_green = 0;
byte paper_blue = 0;

/* current ink (foreground) color, defaults to orange (RGB=F80) if not explicitly set */
byte ink_red = 0xff;
byte ink_green = 0x88;
byte ink_blue = 0x00;

/* internal character set (read-only, in program memory space) */
uint8_t ASCII_Char[62][8] __attribute__((__progmem__)) = {
    { 0x0,0x38,0x44,0x4C,0x54,0x64,0x44,0x38 },   // 0
    { 0x0,0x38,0x10,0x10,0x10,0x10,0x18,0x10 },   // 1
    { 0x0,0x7C,0x8,0x10,0x20,0x40,0x44,0x38 },    // 2
    { 0x0,0x38,0x44,0x40,0x20,0x10,0x20,0x7C },   // 3
    { 0x0,0x20,0x20,0x7C,0x24,0x28,0x30,0x20 },   // 4
    { 0x0,0x38,0x44,0x40,0x40,0x3C,0x4,0x7C },    // 5
    { 0x0,0x38,0x44,0x44,0x3C,0x4,0x8,0x30 },     // 6
    { 0x0,0x8,0x8,0x8,0x10,0x20,0x40,0x7C },      // 7
    { 0x0,0x38,0x44,0x44,0x38,0x44,0x44,0x38 },   // 8
    { 0x0,0x18,0x20,0x40,0x78,0x44,0x44,0x38 },   // 9

    { 0x0,0x44,0x44,0x7C,0x44,0x44,0x28,0x10 },   // A
    { 0x0,0x3C,0x44,0x44,0x3C,0x44,0x44,0x3C },   // B
    { 0x0,0x38,0x44,0x4,0x4,0x4,0x44,0x38 },      // C
    { 0x0,0x1C,0x24,0x44,0x44,0x44,0x24,0x1C },   // D
    { 0x0,0x7C,0x4,0x4,0x3C,0x4,0x4,0x7C },       // E
    { 0x0,0x4,0x4,0x4,0x3C,0x4,0x4,0x7C },        // F
    { 0x0,0x78,0x44,0x44,0x74,0x4,0x44,0x38 },    // G
    { 0x0,0x44,0x44,0x44,0x7C,0x44,0x44,0x44 },   // H
    { 0x0,0x38,0x10,0x10,0x10,0x10,0x10,0x38 },   // I
    { 0x0,0x18,0x24,0x20,0x20,0x20,0x20,0x70 },   // J
    { 0x0,0x44,0x24,0x14,0xC,0x14,0x24,0x44 },    // K
    { 0x0,0x7C,0x4,0x4,0x4,0x4,0x4,0x4 },         // L
    { 0x0,0x44,0x44,0x44,0x54,0x54,0x6C,0x44 },   // M
    { 0x0,0x44,0x44,0x64,0x54,0x4C,0x44,0x44 },   // N
    { 0x0,0x38,0x44,0x44,0x44,0x44,0x44,0x38 },   // O
    { 0x0,0x4,0x4,0x4,0x3C,0x44,0x44,0x3C },      // P
    { 0x0,0x58,0x24,0x54,0x44,0x44,0x44,0x38 },   // Q
    { 0x0,0x44,0x24,0x14,0x3C,0x44,0x44,0x3C },   // R
    { 0x0,0x3C,0x40,0x40,0x38,0x4,0x4,0x78 },     // S
    { 0x0,0x10,0x10,0x10,0x10,0x10,0x10,0x7C },   // T
    { 0x0,0x38,0x44,0x44,0x44,0x44,0x44,0x44 },   // U
    { 0x0,0x10,0x28,0x44,0x44,0x44,0x44,0x44 },   // V
    { 0x0,0x28,0x54,0x54,0x54,0x44,0x44,0x44 },   // W
    { 0x0,0x44,0x44,0x28,0x10,0x28,0x44,0x44 },   // X
    { 0x0,0x10,0x10,0x10,0x28,0x44,0x44,0x44 },   // Y
    { 0x0,0x7C,0x4,0x8,0x10,0x20,0x40,0x7C },     // Z

    { 0x0,0x78,0x44,0x78,0x40,0x38,0x0,0x0 },     // a
    { 0x0,0x3C,0x44,0x44,0x4C,0x34,0x4,0x4 },     // b
    { 0x0,0x38,0x44,0x4,0x4,0x38,0x0,0x0 },       // c
    { 0x0,0x78,0x44,0x44,0x64,0x58,0x40,0x40 },   // d
    { 0x0,0x38,0x4,0x7C,0x44,0x38,0x0,0x0 },      // e
    { 0x0,0x8,0x8,0x8,0x1C,0x8,0x48,0x30 },       // f
    { 0x38,0x40,0x78,0x44,0x44,0x78,0x0,0x0 },    // g
    { 0x0,0x44,0x44,0x44,0x4C,0x34,0x4,0x4 },     // h
    { 0x0,0x38,0x10,0x10,0x10,0x18,0x0,0x10 },    // i
    { 0x18,0x24,0x20,0x20,0x20,0x30,0x0,0x20 },   // j
    { 0x0,0x24,0x14,0xC,0x14,0x24,0x4,0x4 },      // k
    { 0x0,0x38,0x10,0x10,0x10,0x10,0x10,0x18 },   // l
    { 0x0,0x44,0x44,0x54,0x54,0x2C,0x0,0x0 },     // m
    { 0x0,0x44,0x44,0x44,0x4C,0x34,0x0,0x0 },     // n
    { 0x0,0x38,0x44,0x44,0x44,0x38,0x0,0x0 },     // o
    { 0x4,0x4,0x3C,0x44,0x44,0x3C,0x0,0x0 },      // p
    { 0x40,0x40,0x58,0x64,0x64,0x58,0x0,0x0 },    // q
    { 0x0,0x4,0x4,0x4,0x4C,0x34,0x0,0x0 },        // r
    { 0x0,0x3C,0x40,0x38,0x4,0x38,0x0,0x0 },      // s
    { 0x0,0x30,0x48,0x8,0x8,0x1C,0x8,0x8 },       // t
    { 0x0,0x58,0x64,0x44,0x44,0x44,0x0,0x0 },     // u
    { 0x0,0x10,0x28,0x44,0x44,0x44,0x0,0x0 },     // v
    { 0x0,0x28,0x54,0x54,0x44,0x44,0x0,0x0 },     // w
    { 0x0,0x44,0x28,0x10,0x28,0x44,0x0,0x0 },     // x
    { 0x38,0x40,0x78,0x44,0x44,0x44,0x0,0x0 },    // y
    { 0x0,0x7C,0x8,0x10,0x20,0x7C,0x0,0x0 }       // z
};

uint8_t i2c_buffer[MAX_WIRE_CMD_LEN];

volatile enum {
	wait_for_command,
	process_command,
	synchronize_with_master
} state = wait_for_command;

void handle_request_event ( void )
{
	/* send our current state when the master requests it */
	Wire.send( state );
	if ( state == synchronize_with_master ) state = wait_for_command; /* break the main event loop */
}

void handle_receive_event ( int how_many )
{
	uint8_t index = 0;
	while ( Wire.available() ) {
		i2c_buffer[index] = Wire.receive( );
		if ( index < MAX_WIRE_CMD_LEN ) index++;
	}
	state = process_command;
}

void loop ( void )
{
	void do_periodic_task( void );

	switch ( state ) {
	case wait_for_command:
		/* BIG TASKS:
		   Use the available CPU time to do background tasks, perhaps query RTC/accelerometer and update display accordingly.
		   This is also a good place to update the display if you intend to use the Rainbowduino as a clock, for example. */
		do_periodic_task( );
		break;

	case process_command:
		process_wire_command( );
		state = synchronize_with_master;
		break;

	case synchronize_with_master:
		/* SMALL TASKS:
		   While waiting for the master to synchronize, do really small tasks such as updating button input states, etc.
		   Try not to use i2c bus here as the master may be waiting to use it to obtain a succesful acknowledgement. */
		break;

	default:
		state = wait_for_command;
		break;
	}
}
 
void process_wire_command ( void )
{
}

/* change the gamma array pointer, used during display rendering */
void set_gamma ( uint8_t gamma )
{
		if ( gamma < COUNT_OF_GAMMA_SETS ) chosen_gamma = gamma;
}

void draw_pixel ( uint8_t col, uint8_t row )
{
}

void draw_pixel_rgb ( uint8_t col, uint8_t row, uint8_t red, uint8_t green, uint8_t blue )
{
	register rgb_triplet_t *ptr = &frame_buffers[draw][(row * 8) + col];
	ptr->red = red;
	ptr->green = green;
	ptr->blue = blue;
}

void clear_buffer ( void )
{
	memset( frame_buffers[draw], 0, sizeof(rgbframe_t) );
}

void show_buffer ( void )
{
	uint8_t temp = draw;
	/* do not change 'refresh' here, as it will introduce flicker in the interrupt-driven matrix refresh code */
	draw = view;
	view = temp;
}

void switch_buffer ( uint8_t which )
{
	if ( which < COUNT_OF_FRAMEBUFFERS ) {
		view = which;
		while ( refresh != view ) delay( 10 );
	}
}

void setup ( void )
{
	cli( );
	/* disable timer2 overflow, compare-match-a and compare-match-b interrupts */
	TIMSK2 &= ~((1 << TOIE2)|(1 << OCIE2A)|(1 << OCIE2B));
	/* configure ports, see http://www.arduino.cc/en/Reference/PortManipulation */
	/* ATmega48A/48PA/88A/88PA/168A/168PA/328/328PA datasheet (doc8271.pdf) referred to as "ATmega datasheet" */
	DDRD = DDRC = DDRB = 0xFF;
	/* configure ports data register (see link above) */
	PORTB = PORTD = 0;
	/* init timer2, used to control PWM flashing of the LED matrix */
	/* datasheet: ATmega, Table 17-8 Waveform Generation Mode Bit Description */
	/* WGM [2:0], 011 = Fast PWM */
	/* WGM [2:0], 000 = Normal */
	TCCR2A &= ~((1 << WGM21) | (1 << WGM20));
	TCCR2B &= ~WGM22;
	/* datasheet: ATmega, Table 17-9 Clock Select Bit Description
	   +------+------+------+--------------------+
	   | CS22 | CS21 | CS20 | Description        |
	   |------+------+------+--------------------|
	   |  0   |  0   |  0   | No clock source    |
	   |  0   |  0   |  1   | No prescaling      |
	   |  0   |  1   |  0   | /8                 |
	   |  0   |  1   |  1   | /32                |
	   |  1   |  0   |  0   | /64                |
	   |  1   |  0   |  1   | /128               |
	   |  1   |  1   |  0   | /256               |
	   |  1   |  1   |  1   | /1024              |
	   +------+------+------+--------------------+ */
#if 0
	/* prescale/128 */
	TCCR2B |=   (1 << CS21);
	TCCR2B &= ~((1 << CS22)||(1 << CS20));
	/* prescaler/32 */
	TCCR2B |=  ((1 << CS21)|(1 << CS20));
	TCCR2B &=  ~(1 << CS22);
#endif
	/* prescaler/64 */
	TCCR2B |=   (1 << CS22);
	TCCR2B &= ~((1 << CS21)|(1 << CS20));
	/* internal clock source */
	ASSR &= ~(1 << AS2);
	/* set up i2c subsystem once we're ready to accept commands */
	memset( i2c_buffer, 0, sizeof(i2c_buffer) );
	Wire.begin( I2C_DEVICE_ADDRESS );
	Wire.onReceive( handle_receive_event );
	Wire.onRequest( handle_request_event );
	/* enable timer2 counter2 overflow interrupt */
	TIMSK2 |= (1 << TOIE2);
	TCNT2 = gamma_sets[chosen_gamma][0];
	sei( );
}

/* ============================================= */
/* MBI5168                                       */
/* ============================================= */
#define RAINBOW_PORT_OE			PORTC
#define RAINBOW_OE_BIT			0x08
#define RAINBOW_PORT_SDI		PORTC
#define RAINBOW_SDI_BIT			0x01
#define RAINBOW_PORT_CLK		PORTC
#define RAINBOW_CLK_BIT			0x02
#define RAINBOW_PORT_LE			PORTC
#define RAINBOW_LE_BIT			0x04

#define RAISE(signal)			RAINBOW_PORT_##signal|=RAINBOW_##signal##_BIT
#define LOWER(signal)			RAINBOW_PORT_##signal&=~RAINBOW_##signal##_BIT

#define CLOCK_RISING			LOWER(CLK);RAISE(CLK);
#define shift_data_1			RAISE(SDI)
#define shift_data_0			LOWER(SDI)

ISR ( TIMER2_OVF_vect )
{
	register rgb_triplet_t *ptr;
	register uint8_t bits;
	cli( );
	/* set the flashing time using gamma value table */
	RAISE( OE ); /* NOTE: OE is active-low */
	/* open only the scan_line we're working on and close other scan_lines */
	PORTB = PORTD = 0;
	RAISE( LE );
	/* green */
	ptr = &frame_buffers[refresh][scan_line * 8];
	bits = 8;
	while ( bits-- ) {
		if ( ptr->green > brightness_level ) shift_data_1;
		else shift_data_0;
		ptr++;
		CLOCK_RISING;
	}
	/* red */
	ptr = &frame_buffers[refresh][scan_line * 8];
	bits = 8;
	while ( bits-- ) {
		if ( ptr->red > brightness_level ) shift_data_1;
		else shift_data_0;
		ptr++;
		CLOCK_RISING;
	}
	/* blue */
	ptr = &frame_buffers[refresh][scan_line * 8];
	bits = 8;
	while ( bits-- ) {
		if ( ptr->blue > brightness_level ) shift_data_1;
		else shift_data_0;
		ptr++;
		CLOCK_RISING;
	}
	if (scan_line < 3) {
		PORTB  = (PINB & ~0x07) | (0x04 >> scan_line);
		PORTD  = (PIND & ~0xF8);
	} else {
		PORTB  = (PINB & ~0x07);
		PORTD  = (PIND & ~0xF8) | (0x80 >> (scan_line - 3));
	}
	LOWER( LE );
	LOWER( OE );
	/* after flashing all LED's scan_lines, go back to scan_line 0 and increase the brightness_level */
	scan_line = (++scan_line % 8);
	/* swap buffer when brightness goes back to zero */
	if ( brightness_level++ == 32 ) {
		brightness_level = 0;
		refresh = view;
	}
	sei( );
	TCNT2 = gamma_sets[chosen_gamma][brightness_level];
}

/* vim: set noexpandtab tabstop=4 shiftwidth=4 nolist autoindent cindent: */
